/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xdlink.core.impl;

import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.xuexiang.xdlink._XDLink;
import com.xuexiang.xdlink.component.buffer.IBuffer;
import com.xuexiang.xdlink.component.buffer.impl.CircularBuffer;
import com.xuexiang.xdlink.component.channel.IChannel;
import com.xuexiang.xdlink.component.channel.OnChannelListener;
import com.xuexiang.xdlink.component.channel.entity.ConnectStatusEnum;
import com.xuexiang.xdlink.component.command.IProtocolCommand;
import com.xuexiang.xdlink.component.command.impl.HandleMessageCommand;
import com.xuexiang.xdlink.component.command.impl.ResolveMessageCommand;
import com.xuexiang.xdlink.component.message.IDataInfo;
import com.xuexiang.xdlink.component.message.IMessage;
import com.xuexiang.xdlink.component.message.IRequestMessage;
import com.xuexiang.xdlink.component.protocol.IProtocol;
import com.xuexiang.xdlink.component.protocol.OnProtocolListener;
import com.xuexiang.xdlink.component.protocol.OnReceivedMessageListener;
import com.xuexiang.xdlink.component.protocol.impl.SimpleProtocol;
import com.xuexiang.xdlink.component.resolver.IMessageResolver;
import com.xuexiang.xdlink.component.resolver.impl.SimpleMessageResolver;
import com.xuexiang.xdlink.core.IDeviceConnectCenter;
import com.xuexiang.xdlink.core.OnDeviceConnectListener;
import com.xuexiang.xdlink.core.OnErrorListener;
import com.xuexiang.xdlink.core.function.IDataSaver;
import com.xuexiang.xdlink.core.function.IMonitor;
import com.xuexiang.xdlink.core.function.OnMonitorListener;
import com.xuexiang.xdlink.exception.ChannelException;
import com.xuexiang.xdlink.exception.ConnectException;
import com.xuexiang.xdlink.exception.ProtocolException;
import com.xuexiang.xdlink.logs.DCFLog;
import com.xuexiang.xdlink.util.ConvertTools;
import com.xuexiang.xdlink.util.Utils;

/**
 * 设备连接中心，数据、消息、事件的中转站，管理中心。<br>s
 * 【包含Channel、Buffer、Resolver、Protocol四大组成部分，以及Monitor和DataSaver两个辅助功能模块】
 *
 * @author xuexiang
 * @since 2018/8/23 上午1:54
 */
public class DeviceConnectCenter implements IDeviceConnectCenter, OnChannelListener, OnProtocolListener, OnMonitorListener {
    /**
     * 设备类型
     */
    private String mDeviceType;
    //=============核心四大组件=================//
    /**
     * 通信通道
     */
    private IChannel mChannel;
    /**
     * 数据缓冲器
     */
    private IBuffer mBuffer;
    /**
     * 消息解析器
     */
    private IMessageResolver mResolver;
    /**
     * 协议
     */
    private IProtocol mProtocol;

    //=============辅助组件=================//
    /**
     * 设备超时监控器
     */
    private IMonitor mIMonitor;
    /**
     * 是否接收到数据，接收到数据后，就开启设备超时监控器
     */
    private boolean mHasReceivedData;

    /**
     * 数据保存者，保存通信通道接收到的数据
     */
    private IDataSaver mIDataSaver;

    //=============监听接口=================//
    /**
     * 设备连接的监听接口
     */
    private OnDeviceConnectListener mOnDeviceConnectListener;
    /**
     * 消息接收监听接口
     */
    private OnReceivedMessageListener mOnReceivedMessageListener;
    /**
     * 出错的监听接口
     */
    private OnErrorListener mOnErrorListener;

    private Handler mHandler;

    /**
     * 构造函数
     *
     * @param builder 构建者
     */
    public DeviceConnectCenter(Builder builder) {
        mDeviceType = builder.deviceType;

        mChannel = builder.channel;
        mBuffer = builder.buffer;
        mResolver = builder.resolver;
        mProtocol = builder.protocol;
        mIMonitor = builder.monitor;
        mIDataSaver = builder.dataSaver;

        mOnDeviceConnectListener = builder.onDeviceConnectListener;
        mOnReceivedMessageListener = builder.onReceivedMessageListener;
        mOnErrorListener = builder.onErrorListener;

        mHandler = new Handler();

        initListeners();
    }

    /**
     * 初始化设备监听
     */
    private void initListeners() {
        if (mChannel != null) {
            mChannel.setOnChannelListener(this);
        }
        if (mIMonitor != null) {
            mIMonitor.setOnMonitorListener(this);
        }
        if (mProtocol != null) {
            mProtocol.setOnProtocolListener(this);
        }
    }

    @Override
    public void connectDevice() {
        if (isChannelOpen()) return; //通道已打开的话，就不执行了

        if (mChannel != null) {
            mChannel.openChannel();
        }
    }

    @Override
    public void disConnectDevice() {
        DCFLog.i("Device disConnect，Device type：" + mDeviceType);
        stopMonitor();
        closeChannel();
        recycleComponents();
        recycleListeners();
    }

    @Override
    public boolean sendMessage(@NonNull IRequestMessage message) {
        return onReplyData(message.toBytes());
    }

    @Override
    public void executeCommand(final IProtocolCommand command) {
        if (command.isDelayCommand()) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    doExecuteCommand(command);
                }
            }, command.getDelayTime());
        } else {
            doExecuteCommand(command);
        }
    }

    /**
     * 具体执行协议命令
     *
     * @param command 协议命令
     */
    protected void doExecuteCommand(IProtocolCommand command) {
        try {
            if (mProtocol != null && command != null) {
                mProtocol.executeCommand(command);
            }
        } catch (ProtocolException e) {
            e.printStackTrace();
            onError(e);
        }
    }

    /**
     * 回收组件的资源
     */
    private void recycleComponents() {
        if (mBuffer != null) {
            mBuffer.recycle();
            mBuffer = null;
        }
        if (mIDataSaver != null) {
            mIDataSaver.recycle();
            mIDataSaver = null;
        }
        if (mResolver != null) {
            mResolver.recycle();
            mResolver = null;
        }
        if (mProtocol != null) {
            mProtocol.recycle();
            mProtocol = null;
        }
        mHandler.removeCallbacksAndMessages(null);
    }

    /**
     * 释放所有的监听
     */
    private void recycleListeners() {
        mOnDeviceConnectListener = null;
        mOnReceivedMessageListener = null;
        mOnErrorListener = null;
    }

    //===================Channel的方法===========================//

    /**
     * @return 通信通道是否开启
     */
    @Override
    public boolean isChannelOpen() {
        return mChannel != null && mChannel.isOpen();
    }

    /**
     * 重新打开通信通道
     */
    @Override
    public void reOpenChannel() {
        if (mChannel != null) {
            mChannel.reOpenChannel();
        }
    }

    /**
     * @return 获取重新打开通道的次数
     */
    @Override
    public int getReOpenCount() {
        return mChannel != null ? mChannel.getReOpenCount() : 0;
    }

    /**
     * @return 获取设备的连接状态
     */
    @Nullable
    public ConnectStatusEnum getConnectStatus() {
        return mChannel != null ? mChannel.getConnectStatus() : null;
    }

    /**
     * 关闭通信通道
     */
    private void closeChannel() {
        if (mChannel != null) {
            mChannel.closeChannel();
        }
    }

    //===================Monitor的方法===========================//

    /**
     * 启动监控器
     */
    @Override
    public void startMonitor() {
        if (mIMonitor != null) {
            mIMonitor.startWork();
        }
    }

    /**
     * 关闭监控器
     */
    @Override
    public void stopMonitor() {
        if (mIMonitor != null) {
            mIMonitor.finishWork();
            mIMonitor = null;
        }
    }

    /**
     * @return 监控器是否在运行
     */
    @Override
    public boolean isMonitorRunning() {
        return mIMonitor != null && mIMonitor.isWorking();
    }

    //===================IDataSaver的方法===========================//

    /**
     * 切换数据保存的状态
     *
     * @param isTurnOn
     */
    @Override
    public void switchDataSaveStatus(boolean isTurnOn) {
        if (mIDataSaver != null) {
            mIDataSaver.switchStatus(isTurnOn);
        }
    }

    @Override
    public boolean cancelDataSave() {
        return mIDataSaver == null || mIDataSaver.cancelSave();
    }

    //===================通信通道的监听接口===========================//

    @Override
    public void onConnectStatusChanged(IChannel channel) {
        if (mOnDeviceConnectListener != null) {
            DCFLog.i("The state of channel has changed! Channel： " + channel.getClass().getSimpleName() + ", ConnectStatus:" + channel.getConnectStatus());
            IProtocolCommand command = mOnDeviceConnectListener.onChannelConnectStatusChanged(channel);
            if (command != null) {
                executeCommand(command);
            }
        }
    }

    @Override
    public void onDataReceived(IChannel channel, byte[] data, int available) {
        if (data == null || available <= 0) return;

        //第一次接收到数据后，开启监控器监听
        if (!mHasReceivedData) {
            mHasReceivedData = true;
            startMonitor();
            DCFLog.i(mChannel.toString() + " receiving data for the first time...");
        }
        //监控数据
        if (mIMonitor != null) {
            mIMonitor.processWork();
        }
        //保存数据
        if (mIDataSaver != null) {
            mIDataSaver.saveData(data, available);
        }
        try {
            if (mBuffer != null) {
                mBuffer.putData(data, available);
                mBuffer.resolveData(new ResolveMessageCommand(mResolver));
                mResolver.handleMessage(new HandleMessageCommand(mProtocol));
            }
        } catch (ConnectException e) {
            onError(e);
        }
    }

    //===================协议的监听接口===========================//

    @Override
    public boolean onReplyData(byte[] data) {
        if (isChannelOpen()) {
            try {
                if (data.length > 0 && data.length < 200) { //数据过长的不保存日志
                    DCFLog.d(mChannel.toString() + " send data：" + ConvertTools.bytes2HexString(data));
                }
                mChannel.write(data);
                return true;
            } catch (ConnectException e) {
                onError(e);
                return false;
            }
        } else {
            onError(new ChannelException(ChannelException.WRITE_CHANNEL_CLOSED_ERROR, mChannel.toString() + " is closed，failed to send data：" + ConvertTools.bytes2HexString(data)));
            return false;
        }
    }

    @Override
    public void onReceivedMessage(IMessage message) {
        if (mOnReceivedMessageListener != null) {
            mOnReceivedMessageListener.onReceivedMessage(message);
        }
    }

    @Override
    public void onReceivedDataInfo(IDataInfo dataInfo) {
        if (mOnReceivedMessageListener != null) {
            mOnReceivedMessageListener.onReceivedDataInfo(dataInfo);
        }
    }

    //===================设备连接监控器的监听接口===========================//
    @Override
    public void onExcepted(IMonitor monitor) {
        DCFLog.e("The device connection monitor has been stopped because the device is onExcepted! Channel type：" + monitor.getType());
        closeChannel();
        if (mOnDeviceConnectListener != null) {
            mOnDeviceConnectListener.onDeviceTimeout(this);
        }
    }

    /**
     * 出现异常
     *
     * @param e 连接异常
     */
    private void onError(ConnectException e) {
        if (mOnErrorListener != null) {
            mOnErrorListener.onError(e);
        }
    }

    //===============================get=======================================//

    /**
     * @return 获取设备类型
     */
    @Override
    public String getDeviceType() {
        return mDeviceType;
    }

    /**
     * @return 设备是否接收到数据
     */
    @Override
    public boolean hasReceivedData() {
        return mHasReceivedData;
    }

    /**
     * @return 获取通道
     */
    public IChannel getChannel() {
        return mChannel;
    }

    /**
     * @return 获取缓存区
     */
    public IBuffer getBuffer() {
        return mBuffer;
    }

    /**
     * @return 获取消息解析器
     */
    public IMessageResolver getResolver() {
        return mResolver;
    }

    /**
     * @return 获取协议控制器
     */
    public IProtocol getProtocol() {
        return mProtocol;
    }

    /**
     * @return 获取设备连接监控器
     */
    public IMonitor getMonitor() {
        return mIMonitor;
    }

    /**
     * @return 获取数据保存者
     */
    public IDataSaver getDataSaver() {
        return mIDataSaver;
    }


    //=====================================================================//
    //                              构建者                                  //
    //=====================================================================//

    /**
     * 构建者
     */
    public static class Builder {
        /**
         * 设备的类型（必须设置)【仅用于标记设备，无实际意义】
         */
        String deviceType;
        /**
         * 通信通道（必须设置)
         */
        IChannel channel;
        /**
         * 数据缓冲器
         */
        IBuffer buffer;
        /**
         * 消息解析器
         */
        IMessageResolver resolver;
        /**
         * 协议
         */
        IProtocol protocol;

        //=============辅助组件=================//
        /**
         * 设备数据传输监控器
         */
        IMonitor monitor;
        /**
         * 数据保存者，保存通信通道接收到的数据
         */
        IDataSaver dataSaver;

        //=============监听接口=================//
        /**
         * 设备连接的监听接口（必须设置)
         */
        OnDeviceConnectListener onDeviceConnectListener;
        /**
         * 消息接收监听接口（必须设置)
         */
        OnReceivedMessageListener onReceivedMessageListener;
        /**
         * 出错的监听接口
         */
        OnErrorListener onErrorListener;

        /**
         * 构造方法
         *
         * @param deviceType 设备类型
         */
        public Builder(@NonNull String deviceType) {
            this.deviceType = deviceType;
            buffer = new CircularBuffer(); //默认是环形缓冲区
            onErrorListener = _XDLink.getOnErrorListener(); //默认使用全局出错配置
        }

        /**
         * 设置通信通道【很重要】
         *
         * @param channel
         * @return
         */
        public Builder setChannel(@NonNull IChannel channel) {
            this.channel = channel;
            return this;
        }

        /**
         * 设置缓冲区
         *
         * @param buffer
         * @return
         */
        public Builder setBuffer(@NonNull IBuffer buffer) {
            this.buffer = buffer;
            return this;
        }

        /**
         * 设置消息解析器
         *
         * @param resolver
         * @return
         */
        public Builder setResolver(@NonNull IMessageResolver resolver) {
            this.resolver = resolver;
            return this;
        }

        /**
         * 设置协议控制器
         *
         * @param protocol
         * @return
         */
        public Builder setProtocol(@NonNull IProtocol protocol) {
            this.protocol = protocol;
            return this;
        }

        /**
         * 设置设备连接监控器
         *
         * @param monitor
         * @return
         */
        public Builder setMonitor(@NonNull IMonitor monitor) {
            this.monitor = monitor;
            return this;
        }

        /**
         * 设置数据保存者【不设置将不保存数据】
         *
         * @param dataSaver
         * @return
         */
        public Builder setDataSaver(@NonNull IDataSaver dataSaver) {
            this.dataSaver = dataSaver;
            return this;
        }

        /**
         * 设置设备的监听器
         *
         * @param onDeviceConnectListener   设备连接的监听器
         * @param onReceivedMessageListener 消息、数据接收的监听器
         * @return
         */
        public Builder setDeviceListeners(@NonNull OnDeviceConnectListener onDeviceConnectListener, @NonNull OnReceivedMessageListener onReceivedMessageListener) {
            this.onDeviceConnectListener = onDeviceConnectListener;
            this.onReceivedMessageListener = onReceivedMessageListener;
            return this;
        }

        /**
         * 设置设备连接的监听
         *
         * @param onDeviceConnectListener
         * @return
         */
        public Builder setOnDeviceConnectListener(@NonNull OnDeviceConnectListener onDeviceConnectListener) {
            this.onDeviceConnectListener = onDeviceConnectListener;
            return this;
        }

        /**
         * 设置消息、数据接收的监听
         *
         * @param onReceivedMessageListener
         * @return
         */
        public Builder setOnReceivedMessageListener(@NonNull OnReceivedMessageListener onReceivedMessageListener) {
            this.onReceivedMessageListener = onReceivedMessageListener;
            return this;
        }

        /**
         * 设置出错的监听
         *
         * @param onErrorListener
         * @return
         */
        public Builder setOnErrorListener(@NonNull OnErrorListener onErrorListener) {
            this.onErrorListener = onErrorListener;
            return this;
        }

        /**
         * @return 构建设备连接中心
         */
        public DeviceConnectCenter build() {
            Utils.requireNonNull(channel, "Channel must be set!");
            Utils.requireNonNull(onDeviceConnectListener, "onDeviceConnectListener must be set!");
            Utils.requireNonNull(onReceivedMessageListener, "onReceivedMessageListener must be set!");

            if (resolver == null) {
                resolver = new SimpleMessageResolver();
            }
            if (protocol == null) {
                protocol = new SimpleProtocol();
            }

            return new DeviceConnectCenter(this);
        }

        /**
         * @return 构建设备连接中心并进行连接
         */
        public DeviceConnectCenter connect() {
            DeviceConnectCenter connectCenter = build();
            connectCenter.connectDevice();
            return connectCenter;
        }
    }


}
